import glob
import os
import subprocess
import multiprocessing
import argparse
import requests
import logging
from multiprocessing import Value, Lock

# 配置日志格式(包含时间戳)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 配置SonarQube服务器信息
SONARQUBE_URL = "http://10.10.3.247:9000"
API_TOKEN = "sqa_ff723f9dc9ff2cf217261474efca733352c0b43a"

def find_git_projects(base_dir):
    """查找最多两层目录中的.git项目目录"""
    projects = []
    for root, dirs, _ in os.walk(base_dir):
        current_depth = root[len(base_dir):].count(os.sep)
        if current_depth >= 2:
            del dirs[:]  # 超过两层则不再深入
            continue
        git_path = os.path.join(root, ".git")
        if os.path.exists(git_path):
            projects.append(root)
    return projects

def check_project_exists(project_key, api_error_count, api_lock):
    try:
        response = requests.get(
            f"{SONARQUBE_URL}/api/projects/search",
            params={"projects": project_key},
            headers={"Authorization": f"Bearer {API_TOKEN}"},
            timeout=10
        )
        response.raise_for_status()
        components = response.json().get("components", [])
        return any(p.get("key") == project_key for p in components)
    except requests.exceptions.HTTPError as e:
        if response.status_code == 403:
            with api_lock:
                api_error_count.value += 1
            logging.error(f"检查项目失败：{project_key}，无权限访问 SonarQube API")
            return True  # 假设存在，避免重复创建
        else:
            logging.error(f"检查项目失败：{project_key}，HTTP错误：{e}，响应：{response.text}")
            return False
    except Exception as e:
        logging.error(f"检查项目失败：{project_key}，网络错误：{str(e)}")
        return True  # 假设存在，避免风险

def create_project(project_key, project_name):
    """创建新项目(增强错误处理)"""
    try:
        response = requests.post(
            f"{SONARQUBE_URL}/api/projects/create",
            params={
                "name": project_name,
                "project": project_key,
                "visibility": "public"
            },
            auth=(API_TOKEN, ""),
            timeout=10
        )
        if response.status_code == 200:
            logging.info(f"✅ 成功创建项目：{project_name}(Key: {project_key})")
        else:
            logging.error(f"❌ 创建失败：{project_name}(Key: {project_key})，错误：{response.text}")
    except requests.exceptions.RequestException as e:
        logging.error(f"创建失败：{project_name}，网络错误：{str(e)}")

def analyze_project(project_path, api_error_count, api_lock):
    """执行SonarQube分析(多进程安全版本)"""
    app = os.path.basename(project_path)
    logging.info(f"正在处理项目：{app}")
    
    # 检测项目类型
    java_src_dir = os.path.join(project_path, 'src/main/java')
    java_bin_dir = os.path.join(project_path, 'target/classes')
    has_java = os.path.exists(java_src_dir) and os.path.isdir(java_src_dir)
    has_python = bool(glob.glob(os.path.join(project_path, '**', '*.py'), recursive=True))

    # 确定项目类型
    if not (has_java or has_python):
        logging.warning(f"未检测到支持的文件类型(.java 或 .py)，跳过分析：{app}")
        return

    # 检查项目是否存在
    try:
        exists = check_project_exists(app, api_error_count, api_lock)
    except Exception as e:
        logging.error(f"检查项目失败：{app}，错误：{str(e)}")
        exists = True  # 假设存在，避免风险

    if not exists:
        create_project(app, app)
    else:
        logging.info(f"⚠️ 项目 {app} 已存在，跳过创建")

    # 执行分析命令
    try:
        if has_java:
            cmd = [
                'sonar-scanner',
                f'-Dsonar.projectKey={app}',
                f'-Dsonar.projectBaseDir={project_path}',
                f'-Dsonar.sources={java_src_dir}',
                f'-Dsonar.java.binaries={java_bin_dir}',
                '-Dsonar.host.url=http://10.10.3.247:9000',
                '-Dsonar.token=sqa_ff723f9dc9ff2cf217261474efca733352c0b43a',
                '-Dsonar.scm.provider=git'
            ]
        else:  # Python项目
            cmd = [
                'sonar-scanner',
                f'-Dsonar.projectKey={app}',
                f'-Dsonar.projectBaseDir={project_path}',
                f'-Dsonar.sources={project_path}',
                '-Dsonar.host.url=http://10.10.3.247:9000',
                '-Dsonar.token=sqa_ff723f9dc9ff2cf217261474efca733352c0b43a',
                '-Dsonar.python.version=3.12',
                '-Dsonar.scm.provider=git'
            ]

        logging.info(f"执行命令：{' '.join(cmd)}")
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode != 0:
            logging.error(f"❌ 分析失败：{app}\n错误信息：{result.stderr}")
        else:
            logging.info(f"✅ 完成分析：{app}")
    except Exception as e:
        logging.error(f"❌ 分析失败：{app}，错误：{str(e)}")

def main(api_error_count, api_lock):
    parser = argparse.ArgumentParser()
    parser.add_argument('--workers', type=int, default=multiprocessing.cpu_count(), 
                        help='并发处理的进程数(默认为CPU核心数)')
    args = parser.parse_args()

    base_dir = "/data/jenkins/jenkins_home/workspace/"
    projects = find_git_projects(base_dir)

    if not projects:
        logging.error("未找到包含.git的项目！")
        return

    total = len(projects)
    logging.info(f"共找到 {total} 个项目，开始分析...")
    
    # 初始化进度计数器
    counter = Value('i', 0)
    lock = Lock()

    def update_counter():
        with lock:
            counter.value += 1
            logging.info(f"已完成 {counter.value}/{total} 项目")

    with multiprocessing.Pool(processes=args.workers) as pool:
        tasks = []
        for project in projects:
            task = pool.apply_async(
                analyze_project, 
                (project, api_error_count, api_lock), 
                callback=lambda _: update_counter()
            )
            tasks.append(task)
        
        # 等待所有任务完成
        for task in tasks:
            task.get()

    logging.info(f"所有项目分析完成！共处理 {total} 个项目")
    logging.info(f"总计因权限问题失败的项目检查次数：{api_error_count.value}")

if __name__ == "__main__":
    # 初始化共享计数器和锁
    api_error_count = Value('i', 0)
    api_lock = Lock()
    
    main(api_error_count, api_lock)